package com.xcw.jac.multithreadeddownloader.core;

import com.xcw.jac.multithreadeddownloader.constant.Constant;
import com.xcw.jac.multithreadeddownloader.utils.FileUtils;
import com.xcw.jac.multithreadeddownloader.utils.HttpUtils;
import com.xcw.jac.multithreadeddownloader.utils.LogUtils;

import java.io.*;
import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.concurrent.*;

/**
 * @author xcw
 * @date 2023/8/10 11:13
 * @description 下载器
 */
public class Downloader {

    public static ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
    // 线程池对象
    public static ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(Constant.DEFAULT_THREAD_NUM, Constant.DEFAULT_THREAD_NUM,
            0, TimeUnit.SECONDS, new ArrayBlockingQueue<>(Constant.DEFAULT_THREAD_NUM));

    // 建立CountDownLatch对象
    public static CountDownLatch countDownLatch = new CountDownLatch(Constant.DEFAULT_THREAD_NUM);

    // 下载过程
    public static Boolean status = true;

    public static void download(String url) {
        download(url, null);
    }

    public static void download(String url, String downloadPath) {
        // 获取文件名
        String httpFileName = HttpUtils.getHttpFileName(url);
        httpFileName = downloadPath == null ? Constant.DEFAULT_PATH + httpFileName : downloadPath + httpFileName;
        operate(httpFileName, url);
    }

    /**
     * 实际下载操作
     * @param fileName
     * @param url
     */
    private static void operate(String fileName, String url) {

        // 获取本地文件大小
        Long localFileSize = FileUtils.getFileContentLength(fileName);

        HttpURLConnection httpURLConnection = null;
        DownLoadInfoThread downLoadInfoThread = null;
        try {
            httpURLConnection = HttpUtils.getHttpURLConnection(url);
            // 获取下载文件总大小
            int contentLength = httpURLConnection.getContentLength();

            if (localFileSize >= contentLength) {
                LogUtils.info("{}文件已经存在，无需重新下载", fileName);
                status = false;
                return;
            }

//            downLoadInfoThread = new DownLoadInfoThread(Long.valueOf(contentLength));
//
//            // 将任务交给线程执行
//            scheduledExecutorService.scheduleAtFixedRate(downLoadInfoThread, 1,1,TimeUnit.SECONDS);

            // 切分任务
            ArrayList<Future> list = new ArrayList<>();
            spilt(url, list);
            countDownLatch.await();
            // 合并文件
            if (merge(fileName)) {
                clearTemp(fileName);
            }
        } catch (FileNotFoundException e) {
            LogUtils.error("下载文件不存在{}", url);
        } catch (Exception e) {
            LogUtils.error("下载失败");
        } finally {
            if (status) {
                System.out.println("\r");
                System.out.println("下载完成");
                status =true;
            }
            if (httpURLConnection != null) {
                httpURLConnection.disconnect();
            }
            scheduledExecutorService.shutdownNow();

            // 关闭线程池
            poolExecutor.shutdown();
        }
    }

    /**
     * 文件分割
     * @param url
     * @param futureList
     */
    public static void spilt(String url, ArrayList<Future> futureList) {
        // 获取文件的大小
        try {
            long httpFileContentLength = HttpUtils.getHttpFileContentLength(url);
            // 计算切分后文件的大小
            long size = httpFileContentLength / Constant.DEFAULT_THREAD_NUM;

            // 计算分块个数
            for (int i = 0; i < Constant.DEFAULT_THREAD_NUM; i++) {
                // 计算开始位置
                long startPos = i * size;
                // 计算结束位置
                long endPos;
                if (i == Constant.DEFAULT_THREAD_NUM - 1) {
                    endPos = 0;
                } else {
                    endPos = startPos + size;
                }
                // 如果不是第一块 起始位置要加一
                if (startPos != 0) {
                    startPos++;
                }
                // 创建下载任务
                DownloaderTask downloaderTask = new DownloaderTask(url, startPos, endPos, i, countDownLatch);
                // 将任务提交到线程池
                Future<Boolean> future = poolExecutor.submit(downloaderTask);
                futureList.add(future);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    public static Boolean merge(String fileName) {
        LogUtils.info("开始合并文件{}", fileName);
        byte[] buffer = new byte[Constant.BYTE_SIZE];
        int len  = -1;
        try (RandomAccessFile accessFile = new RandomAccessFile(fileName, "rw")) {
            for (int i = 0; i < Constant.DEFAULT_THREAD_NUM; i++) {
                try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(fileName + Constant.FILE_SUFFIX + i))) {
                    while ((len = bis.read(buffer)) != -1) {
                        accessFile.write(buffer, 0, len);
                    }
                }
            }
            LogUtils.info("文件合并完毕{}", fileName);
        } catch (FileNotFoundException e) {
            LogUtils.info("文件合并过程中，文件找不到{}", fileName);
            return false;
        } catch (Exception e) {
            LogUtils.info("文件合并失败{}", fileName);
            return false;
        }
        return true;
    }

    /**
     * 删除临时文件
     * @param fileName
     * @return
     */
    public static Boolean clearTemp(String fileName) {
        for (int i = 0; i < Constant.DEFAULT_THREAD_NUM; i++) {
            File file = new File(fileName + Constant.FILE_SUFFIX + i);
            file.delete();
        }
        return true;
    }
}
